﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using EnvRide.Core.ObjectModel;
using System.IO;
using System.Xml;
using CodeMadeSimple.Common.Tests;
using CodeMadeSimple.Common.Validation;
using CodeMadeSimple.Common.Tests.Validation;

namespace EnvRide.Core.UnitTests
{
    [TestFixture]
    public class ElementOverrideTests : OverrideTests
    {
        private const string FILE_PATH = "ElementOverrideTests.xml";

        [Test]
        public void Process()
        {
            ElementOverride elemOver = new ElementOverride();

            Process(elemOver);
        }

        [Test]
        public void RemoveElement()
        {
            EnvRide.Core.ObjectModel.Environment env = Helper.CreateDummyEnvironment();

            ElementOverride elemOver = new ElementOverride();
            elemOver.Environment = env;
            elemOver.Xpath = "//child";
            elemOver.OverrideMode = ElementOverrideMode.RemoveElement;

            XmlDocument doc = new XmlDocument();
            EnvRideProcessArguments args = new EnvRideProcessArguments { Document = doc };
            Helper.AddPropertyContext(args);
            doc.Load(FILE_PATH);

            Assert.AreEqual(2, doc.SelectNodes("//child").Count);

            elemOver.Process(args);

            AssertHasNoErrors(args);

            Assert.AreEqual(0, doc.SelectNodes("//child").Count);

            doc.Load(FILE_PATH);

            elemOver.If = "false";

            elemOver.Process(args);

            AssertHasNoErrors(args);

            Assert.AreEqual(2, doc.SelectNodes("//child").Count);
        }
        
        [Test]
        public void ReplaceElement()
        {
            EnvRide.Core.ObjectModel.Environment env = Helper.CreateDummyEnvironment();

            ElementOverride elemOver = new ElementOverride();
            elemOver.Environment = env;
            elemOver.Xpath = "//child";
            elemOver.OverrideMode = ElementOverrideMode.ReplaceElement;
            elemOver.NewXml = "<replacedChild att=\"AttInChild\"><replacedGrandChild att=\"AttInGrandChild\"/></replacedChild>";

            XmlDocument doc = new XmlDocument();
            EnvRideProcessArguments args = new EnvRideProcessArguments { Document = doc };
            Helper.AddPropertyContext(args);
            doc.Load(FILE_PATH);

            Assert.AreEqual(2, doc.SelectNodes("//child").Count);
            Assert.AreEqual(0, doc.SelectNodes("//replacedChild").Count);

            elemOver.Process(args);

            AssertHasNoErrors(args);

            Assert.AreEqual(0, doc.SelectNodes("//child").Count);

            XmlNodeList replacedNodes = doc.SelectNodes("//replacedChild");

            Assert.AreEqual(2, replacedNodes.Count);

            AssertReplacedNode((XmlElement)replacedNodes[0]);
            AssertReplacedNode((XmlElement)replacedNodes[1]);

            elemOver.NewXml = "<replacedChild att=\"${Prop}\"><replacedGrandChild att=\"${Prop}\"/></replacedChild>";

            doc.Load(FILE_PATH);

            elemOver.Process(args);

            AssertHasNoErrors(args);

            Assert.AreEqual(0, doc.SelectNodes("//child").Count);

            replacedNodes = doc.SelectNodes("//replacedChild");

            Assert.AreEqual(2, replacedNodes.Count);

            AssertReplacedNodeWithPropertyValue((XmlElement)replacedNodes[0]);
            AssertReplacedNodeWithPropertyValue((XmlElement)replacedNodes[1]);
        }

        [Test]
        public void AppendChild()
        {
            EnvRide.Core.ObjectModel.Environment env = Helper.CreateDummyEnvironment();

            ElementOverride elemOver = new ElementOverride();
            elemOver.Environment = env;
            elemOver.Xpath = "//child";
            elemOver.OverrideMode = ElementOverrideMode.AppendChild;
            elemOver.NewXml = "<appendedChild att=\"AttInChild\"><appendedGrandChild att=\"AttInGrandChild\"/></appendedChild>";

            XmlDocument doc = new XmlDocument();
            EnvRideProcessArguments args = new EnvRideProcessArguments { Document = doc };
            Helper.AddPropertyContext(args);
            doc.Load(FILE_PATH);

            Assert.AreEqual(2, doc.SelectNodes("//child").Count);
            Assert.AreEqual(0, doc.SelectNodes("//appendedChild").Count);

            elemOver.Process(args);

            AssertHasNoErrors(args);

            Assert.AreEqual(2, doc.SelectNodes("//child").Count);

            XmlNodeList appendedNodes = doc.SelectNodes("//appendedChild");

            Assert.AreEqual(2, appendedNodes.Count);

            AssertAppendedNode((XmlElement)appendedNodes[0]);
            AssertAppendedNode((XmlElement)appendedNodes[1]);

            elemOver.NewXml = "<appendedChild att=\"${Prop}\"><appendedGrandChild att=\"${Prop}\"/></appendedChild>";

            doc.Load(FILE_PATH);

            elemOver.Process(args);

            AssertHasNoErrors(args);

            appendedNodes = doc.SelectNodes("//appendedChild");

            Assert.AreEqual(2, doc.SelectNodes("//child").Count);
            Assert.AreEqual(2, appendedNodes.Count);

            AssertAppendedNodeWithPropertyValue((XmlElement)appendedNodes[0]);
            AssertAppendedNodeWithPropertyValue((XmlElement)appendedNodes[1]);

            elemOver.If = "false";

            doc.Load(FILE_PATH);

            elemOver.Process(args);

            AssertHasNoErrors(args);

            Assert.AreEqual(2, doc.SelectNodes("//child").Count);
            Assert.AreEqual(0, doc.SelectNodes("//appendedChild").Count);
        }

        private static void AssertAppendedNode(XmlElement node)
        {
            Assert.AreEqual("child", node.ParentNode.Name);
            Assert.AreEqual("AttInChild", node.GetAttribute("att"));
            Assert.AreEqual("AttInGrandChild", ((XmlElement)node.ChildNodes[0]).GetAttribute("att"));
        }

        private static void AssertReplacedNode(XmlElement node)
        {
            Assert.AreEqual("root", node.ParentNode.Name);
            Assert.AreEqual("AttInChild", node.GetAttribute("att"));
            Assert.AreEqual("AttInGrandChild", ((XmlElement)node.ChildNodes[0]).GetAttribute("att"));
        }

        private static void AssertReplacedNodeWithPropertyValue(XmlElement node)
        {
            Assert.AreEqual("root", node.ParentNode.Name);
            Assert.AreEqual(Helper.PROP_VALUE, node.GetAttribute("att"));
            Assert.AreEqual(Helper.PROP_VALUE, ((XmlElement)node.ChildNodes[0]).GetAttribute("att"));
        }

        private static void AssertAppendedNodeWithPropertyValue(XmlElement node)
        {
            Assert.AreEqual("child", node.ParentNode.Name);
            Assert.AreEqual(Helper.PROP_VALUE, node.GetAttribute("att"));
            Assert.AreEqual(Helper.PROP_VALUE, ((XmlElement)node.ChildNodes[0]).GetAttribute("att"));
        }

        [Test]
        public void Serialization()
        {
            ElementOverride attOver = new ElementOverride();
            attOver.If = "If";
            attOver.Name = "Name";
            attOver.NewXml = "NewValue";
            attOver.OverrideMode = ElementOverrideMode.AppendChild;
            attOver.Xpath = "XPath";

            SerializeDeserializeAndCompare(attOver);

            attOver.If = "If2";
            attOver.Name = "Name2";
            attOver.NewXml = "NewValue2";
            attOver.OverrideMode = ElementOverrideMode.RemoveElement;
            attOver.Xpath = "XPath2";

            SerializeDeserializeAndCompare(attOver);
        }

        private static void SerializeDeserializeAndCompare(ElementOverride elemOver)
        {
            ElementOverride elemOver2 = null;

            using (MemoryStream ms = new MemoryStream())
            {
                using (XmlWriter xmlW = XmlWriter.Create(ms))
                {
                    xmlW.WriteStartElement("AttributeOverride");

                    elemOver.WriteXml(xmlW);

                    xmlW.WriteEndElement();
                }

                ms.Position = 0;

                using (XmlReader xmlR = XmlReader.Create(ms))
                {
                    xmlR.Read();

                    xmlR.Read();

                    elemOver2 = new ElementOverride();
                    elemOver2.ReadXml(xmlR);
                }
            }

            AssertEquals(elemOver, elemOver2);
        }

        [Test]
        public void Validate()
        {
            ElementOverride elemOver = new ElementOverride();

            ValidationResult result = elemOver.Validate();

            ValidationResultTests.AssertValidationResult(result, elemOver, false, new IValidationMessage[] { new RequiredValueValidationMessage("Xpath") });

            elemOver.Xpath = "Xpath";

            result = elemOver.Validate();

            ValidationResultTests.AssertValidationResult(result, elemOver, false, false);
        }

        /// <summary>
        /// Asserts two ElementOverride instances that should be equal.
        /// </summary>
        /// <param name="elemOver1">ElementOverride instance one.</param>
        /// <param name="elemOver2">ElementOverride instance two.</param>
        public static void AssertEquals(ElementOverride elemOver1, ElementOverride elemOver2)
        {
            Assert.AreEqual(elemOver1.If, elemOver2.If);
            Assert.AreEqual(elemOver1.Name, elemOver2.Name);
            Assert.AreEqual(elemOver1.NewXml, elemOver2.NewXml);
            Assert.AreEqual(elemOver1.OverrideMode, elemOver2.OverrideMode);
            Assert.AreEqual(elemOver1.Xpath, elemOver2.Xpath);
        } 
    }
}
